package com.fitzframework.core;

import com.fitzframework.core.annotation.Component;
import com.fitzframework.core.annotation.Controller;
import com.fitzframework.core.annotation.Repository;
import com.fitzframework.core.annotation.Service;
import com.fitzframework.util.ClassUtil;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;

import java.lang.annotation.Annotation;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

@Slf4j
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class BeanContainer {

    private volatile boolean loaded = false;

    public boolean isLoaded() {
        return loaded;
    }

    /**
     * 存放所有的bean
     */
    private final ConcurrentHashMap<Class<?>,Object> beanMap = new ConcurrentHashMap<>();

    /**
     * 需要扫描的bean
     */
    private static final List<Class<? extends Annotation>> BEAN_ANNOTATION
            = Arrays.asList(Controller.class, Component.class, Service.class, Repository.class);

    public static BeanContainer getInstance(){
        return ContainerHolder.HOLDER.instance;
    }

    private enum ContainerHolder{
        HOLDER;
        private final BeanContainer instance;
        ContainerHolder(){
            instance = new BeanContainer();
        }
    }

    public synchronized void loadBeans(String basePackage){
        if (isLoaded()){
            log.warn("容器已经被加载过了");
            return;
        }
        Set<Class<?>> classSet = ClassUtil.extractPackageClass(basePackage);
        if (CollectionUtils.isEmpty(classSet)){
            log.warn("从该路径下获取不到任何信息:{}",basePackage);
            return;
        }

        for (Class<?> clazz : classSet){
            for (Class<? extends Annotation> annotation : BEAN_ANNOTATION){
                //是否被对应的注解标记
                if (clazz.isAnnotationPresent(annotation)){
                    //将目标类自身作为key，具体实例作为值放入beanMap
                    beanMap.put(clazz,ClassUtil.newInstance(clazz,true));
                }
            }
        }
        loaded = true;
    }
    /**
     * 获取bean的数量
     * @return
     */
    public int size(){
        return beanMap.size();
    }

    /**
     * 向容器中添加bean
     * @param clazz
     * @param bean
     * @return
     */
    public Object addBean(Class<?> clazz,Object bean){
        return beanMap.put(clazz, bean);
    }

    /**
     * 删除容器中的bean
     * @param clazz
     * @return
     */
    public Object removeBean(Class<?> clazz){
        return beanMap.remove(clazz);
    }

    /**
     * 通过class获取bean
     * @param clazz
     * @return
     */
    public Object getBean(Class<?> clazz){
        return beanMap.get(clazz);
    }

    /**
     * 获取所有class
     * @return
     */
    public Set<Class<?>> getClasses(){
        return beanMap.keySet();
    }
    /**
     * 获取所有Bean集合
     *
     * @return Bean集合
     */
    public Set<Object> getBeans(){
        return new HashSet<>( beanMap.values());
    }

    /**
     * 根据注解查找bean
     * @param annotation
     * @return
     */
    public Set<Class<?>> getClassesByAnnotation(Class<? extends Annotation> annotation){
        //1.获取beanMap的所有class对象
        Set<Class<?>> keySet = getClasses();
        if(CollectionUtils.isEmpty(keySet)){
            log.warn("在容器中没找到该类型的bean:{}",annotation);
            return null;
        }
        //2.通过注解筛选被注解标记的class对象，并添加到classSet里
        Set<Class<?>> classSet = new HashSet<>();
        for(Class<?> clazz : keySet){
            //类是否有相关的注解标记
            if(clazz.isAnnotationPresent(annotation)){
                classSet.add(clazz);
            }
        }
        return classSet.size() > 0? classSet: null;
    }

    /**
     * 通过接口或者父类获取实现类或者子类的Class集合，不包括其本身
     *
     * @param interfaceOrClass 接口Class或者父类Class
     * @return Class集合
     */
    public Set<Class<?>> getClassesBySuper(Class<?> interfaceOrClass){
        //1.获取beanMap的所有class对象
        Set<Class<?>> keySet = getClasses();
        if(CollectionUtils.isEmpty(keySet)){
            log.warn("nothing in beanMap");
            return null;
        }
        //2.判断keySet里的元素是否是传入的接口或者类的子类，如果是，就将其添加到classSet里
        Set<Class<?>> classSet = new HashSet<>();
        for(Class<?> clazz : keySet){
            //判断keySet里的元素是否是传入的接口或者类的子类
            if(interfaceOrClass.isAssignableFrom(clazz) && !clazz.equals(interfaceOrClass)){
                classSet.add(clazz);
            }
        }
        return classSet.size() > 0? classSet: null;
    }

}
